2004年11月17日
川俣晶の縁側ソフトウェア技術雑記 total 11723 count

C#において、なぜIntPtr ptr = new IntPtr (0x80000000);がオーバーフロー例外になるのか

Written By: 川俣 晶連絡先

 実に私好みの面白い話題なので、勝手に取り上げさせて頂きました。

 [技術情報][.NET] IntPtr の値域?で渋木さんが嘆いているので、ちょっと調べてみました。

 C#において、なぜIntPtr ptr = new IntPtr (0x80000000);がオーバーフロー例外になるのかです。

現象の確認 §

 まず、こんなコードを走らせてみます。

using System;

namespace ConsoleApplication41

{

    class Class1

    {

        [STAThread]

        static void Main(string[] args)

        {

            IntPtr ptr = new IntPtr (0x80000000);

        }

    }

}

 確かに例外を投げてしまいます。

そもそも0x80000000ってintに代入できるのか? §

 そもそも0x80000000ってintに代入できるのだろうか、という当然の疑問が発生します。

 というわけで、こんなコードを書いてみました。

using System;

namespace ConsoleApplication41

{

    class Class1

    {

        [STAThread]

        static void Main(string[] args)

        {

            int a = 0x80000000;

        }

    }

}

 これは一発でコンパイルエラーになりました。

d:\w\test\ConsoleApplication41\Class1.cs(10): 型 'uint' を型 'int' に暗黙的に変換できません。

 というわけで、0x80000000はuint型の値で、正の数であることが推測されます。int型では、この正の値を表現することができません。ゆえに、変換はできないのが当然の結末です。

では、なぜ先の例ではコンパイルエラーにならないのか? §

 では、なぜ先の例ではコンパイルエラーにならないのでしょうか?

 それは、IntPtrのコンストラクタにはint64を引数に取るものがあり、それが使われているためだと分かりました。

 64bit符合付き整数であれば、0x80000000 (2147483648)という正の数を扱えます。

 しかし、これは32bit符合付き整数では扱えません。つまり、IntPtrで扱う32bitシステムのポインターは-2147483648~2147483647の範囲の値であることが期待されているにもかかわらず、0x80000000 (2147483648)という値を使おうとしても範囲外であるため、例外が起こるわけですね。

ベテランプログラマの落とし穴か? (2004年11月17日13時45分頃訂正) §

 (この部分は、不適切なサンプル ソースだったので2004年11月17日13時45分頃訂正しました)。

 このような挙動は、CやC++に慣れ親しんだプログラマにとって落とし穴になりがちな問題かもしれません。

 たとえば、Visual Studio.NETでC++のWin32コンソールアプリケーションとして、ちょいちょいと書いて実行すると……。

int _tmain(int argc, _TCHAR* argv[])

{

    unsigned int a = 2147483648;

    signed int b = -2147483648;

    printf("%d",a == b );

    return 0;

}

 変数aの値は正数。変数bの値は負数であるはずなのに、1(一致)という結果が示されます。

 このような符合のルーズさを、C#は継承していないということですね。

 C#で似たようなコードを書いてみます。

using System;

namespace ConsoleApplication41

{

    class Class1

    {

        [STAThread]

        static void Main(string[] args)

        {

            uint a = 2147483648;

            int b = -2147483648;

            Console.WriteLine( a == b );

        }

    }

}

 この結果はFalse(不一致)となります。